home *** CD-ROM | disk | FTP | other *** search
/ Apple Developer Connection Student Program / ADC Tools Sampler CD Disk 3 1999.iso / Metrowerks CodeWarrior / Java Support / Java_Source / Java2 / src / java / util / AbstractMap.java < prev    next >
Encoding:
Java Source  |  1999-05-28  |  20.6 KB  |  579 lines  |  [TEXT/CWIE]

  1. /*
  2.  * @(#)AbstractMap.java    1.17 98/09/30
  3.  *
  4.  * Copyright 1997, 1998 by Sun Microsystems, Inc.,
  5.  * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
  6.  * All rights reserved.
  7.  *
  8.  * This software is the confidential and proprietary information
  9.  * of Sun Microsystems, Inc. ("Confidential Information").  You
  10.  * shall not disclose such Confidential Information and shall use
  11.  * it only in accordance with the terms of the license agreement
  12.  * you entered into with Sun.
  13.  */
  14.  
  15. package java.util;
  16. import java.util.Map.Entry;
  17.  
  18. /**
  19.  * This class provides a skeletal implementation of the <tt>Map</tt>
  20.  * interface, to minimize the effort required to implement this interface. <p>
  21.  *
  22.  * To implement an unmodifiable map, the programmer needs only to extend this
  23.  * class and provide an implementation for the <tt>entrySet</tt> method, which
  24.  * returns a set-view of the map's mappings.  Typically, the returned set
  25.  * will, in turn, be implemented atop <tt>AbstractSet</tt>.  This set should
  26.  * not support the <tt>add</tt> or <tt>remove</tt> methods, and its iterator
  27.  * should not support the <tt>remove</tt> method.<p>
  28.  *
  29.  * To implement a modifiable map, the programmer must additionally override
  30.  * this class's <tt>put</tt> method (which otherwise throws an
  31.  * <tt>UnsupportedOperationException</tt>), and the iterator returned by
  32.  * <tt>entrySet().iterator()</tt> must additionally implement its
  33.  * <tt>remove</tt> method.<p>
  34.  *
  35.  * The programmer should generally provide a void (no argument) and map
  36.  * constructor, as per the recommendation in the <tt>Map</tt> interface
  37.  * specification.<p>
  38.  *
  39.  * The documentation for each non-abstract methods in this class describes its
  40.  * implementation in detail.  Each of these methods may be overridden if the
  41.  * map being implemented admits a more efficient implementation.
  42.  *
  43.  * @author  Josh Bloch
  44.  * @version 1.17 09/30/98
  45.  * @see Map
  46.  * @see Collection
  47.  * @since JDK1.2
  48.  */
  49.  
  50. public abstract class AbstractMap implements Map {
  51.     /**
  52.      * Sole constructor.  (For invocation by subclass constructors, typically
  53.      * implicit.)
  54.      */
  55.     protected AbstractMap() {
  56.     }
  57.  
  58.     // Query Operations
  59.  
  60.     /**
  61.      * Returns the number of key-value mappings in this map.  If the map
  62.      * contains more than <tt>Integer.MAX_VALUE</tt> elements, returns
  63.      * <tt>Integer.MAX_VALUE</tt>.<p>
  64.      *
  65.      * This implementation returns <tt>entrySet().size()</tt>.
  66.      *
  67.      * @return the number of key-value mappings in this map.
  68.      */
  69.     public int size() {
  70.     return entrySet().size();
  71.     }
  72.  
  73.     /**
  74.      * Returns <tt>true</tt> if this map contains no key-value mappings. <p>
  75.      *
  76.      * This implementation returns <tt>size() == 0</tt>.
  77.      *
  78.      * @return <tt>true</tt> if this map contains no key-value mappings.
  79.      */
  80.     public boolean isEmpty() {
  81.     return size() == 0;
  82.     }
  83.  
  84.     /**
  85.      * Returns <tt>true</tt> if this map maps one or more keys to this value.
  86.      * More formally, returns <tt>true</tt> if and only if this map contains
  87.      * at least one mapping to a value <tt>v</tt> such that <tt>(value==null ?
  88.      * v==null : value.equals(v))</tt>.  This operation will probably require
  89.      * time linear in the map size for most implementations of map.<p>
  90.      *
  91.      * This implementation iterates over entrySet() searching for an entry
  92.      * with the specified value.  If such an entry is found, <tt>true</tt> is
  93.      * returned.  If the iteration terminates without finding such an entry,
  94.      * <tt>false</tt> is returned.  Note that this implementation requires
  95.      * linear time in the size of the map.
  96.      *
  97.      * @param value value whose presence in this map is to be tested.
  98.      * 
  99.      * @return <tt>true</tt> if this map maps one or more keys to this value.
  100.      */
  101.     public boolean containsValue(Object value) {
  102.     Iterator i = entrySet().iterator();
  103.     if (value==null) {
  104.         while (i.hasNext()) {
  105.         Entry e = (Entry) i.next();
  106.         if (e.getValue()==null)
  107.             return true;
  108.         }
  109.     } else {
  110.         while (i.hasNext()) {
  111.         Entry e = (Entry) i.next();
  112.         if (value.equals(e.getValue()))
  113.             return true;
  114.         }
  115.     }
  116.     return false;
  117.     }
  118.  
  119.     /**
  120.      * Returns <tt>true</tt> if this map contains a mapping for the specified
  121.      * key. <p>
  122.      *
  123.      * This implementation iterates over <tt>entrySet()</tt> searching for an
  124.      * entry with the specified key.  If such an entry is found, <tt>true</tt>
  125.      * is returned.  If the iteration terminates without finding such an
  126.      * entry, <tt>false</tt> is returned.  Note that this implementation
  127.      * requires linear time in the size of the map; many implementations will
  128.      * override this method.
  129.      *
  130.      * @param key key whose presence in this map is to be tested.
  131.      * @return <tt>true</tt> if this map contains a mapping for the specified
  132.      *            key.
  133.      * 
  134.      * @throws ClassCastException if the specified key is of an inappropriate
  135.      *           type for this map.
  136.      * 
  137.      * @throws NullPointerException key is <tt>null</tt> and this map does not
  138.      *            not permit <tt>null</tt> keys.
  139.      */
  140.     public boolean containsKey(Object key) {
  141.     Iterator i = entrySet().iterator();
  142.     if (key==null) {
  143.         while (i.hasNext()) {
  144.         Entry e = (Entry) i.next();
  145.         if (e.getKey()==null)
  146.             return true;
  147.         }
  148.     } else {
  149.         while (i.hasNext()) {
  150.         Entry e = (Entry) i.next();
  151.         if (key.equals(e.getKey()))
  152.             return true;
  153.         }
  154.     }
  155.     return false;
  156.     }
  157.  
  158.     /**
  159.      * Returns the value to which this map maps the specified key.  Returns
  160.      * <tt>null</tt> if the map contains no mapping for this key.  A return
  161.      * value of <tt>null</tt> does not <i>necessarily</i> indicate that the
  162.      * map contains no mapping for the key; it's also possible that the map
  163.      * explicitly maps the key to <tt>null</tt>.  The containsKey operation
  164.      * may be used to distinguish these two cases. <p>
  165.      *
  166.      * This implementation iterates over <tt>entrySet()</tt> searching for an
  167.      * entry with the specified key.  If such an entry is found, the entry's
  168.      * value is returned.  If the iteration terminates without finding such an
  169.      * entry, <tt>null</tt> is returned.  Note that this implementation
  170.      * requires linear time in the size of the map; many implementations will
  171.      * override this method.
  172.      *
  173.      * @param key key whose associated value is to be returned.
  174.      * @return the value to which this map maps the specified key.
  175.      * 
  176.      * @throws ClassCastException if the specified key is of an inappropriate
  177.      *           type for this map.
  178.      * 
  179.      * @throws NullPointerException if the key is <tt>null</tt> and this map
  180.      *          does not not permit <tt>null</tt> keys.
  181.      * 
  182.      * @see #containsKey(Object)
  183.      */
  184.     public Object get(Object key) {
  185.     Iterator i = entrySet().iterator();
  186.     if (key==null) {
  187.         while (i.hasNext()) {
  188.         Entry e = (Entry) i.next();
  189.         if (e.getKey()==null)
  190.             return e.getValue();
  191.         }
  192.     } else {
  193.         while (i.hasNext()) {
  194.         Entry e = (Entry) i.next();
  195.         if (key.equals(e.getKey()))
  196.             return e.getValue();
  197.         }
  198.     }
  199.     return null;
  200.     }
  201.  
  202.  
  203.     // Modification Operations
  204.  
  205.     /**
  206.      * Associates the specified value with the specified key in this map
  207.      * (optional operation).  If the map previously contained a mapping for
  208.      * this key, the old value is replaced.<p>
  209.      *
  210.      * This implementation always throws an
  211.      * <tt>UnsupportedOperationException</tt>.
  212.      *
  213.      * @param key key with which the specified value is to be associated.
  214.      * @param value value to be associated with the specified key.
  215.      * 
  216.      * @return previous value associated with specified key, or <tt>null</tt>
  217.      *           if there was no mapping for key.  (A <tt>null</tt> return can
  218.      *           also indicate that the map previously associated <tt>null</tt>
  219.      *           with the specified key, if the implementation supports
  220.      *           <tt>null</tt> values.)
  221.      * 
  222.      * @throws UnsupportedOperationException if the <tt>put</tt> operation is
  223.      *              not supported by this map.
  224.      * 
  225.      * @throws ClassCastException if the class of the specified key or value
  226.      *               prevents it from being stored in this map.
  227.      * 
  228.      * @throws IllegalArgumentException if some aspect of this key or value *
  229.      *            prevents it from being stored in this map.
  230.      * 
  231.      * @throws NullPointerException this map does not permit <tt>null</tt>
  232.      *            keys or values, and the specified key or value is
  233.      *            <tt>null</tt>.
  234.      */
  235.     public Object put(Object key, Object value) {
  236.     throw new UnsupportedOperationException();
  237.     }
  238.  
  239.     /**
  240.      * Removes the mapping for this key from this map if present (optional
  241.      * operation). <p>
  242.      *
  243.      * This implementation iterates over <tt>entrySet()</tt> searching for an
  244.      * entry with the specified key.  If such an entry is found, its value is
  245.      * obtained with its <tt>getValue</tt> operation, the entry is is removed
  246.      * from the Collection (and the backing map) with the iterator's
  247.      * <tt>remove</tt> operation, and the saved value is returned.  If the
  248.      * iteration terminates without finding such an entry, <tt>null</tt> is
  249.      * returned.  Note that this implementation requires linear time in the
  250.      * size of the map; many implementations will override this method.
  251.      *
  252.      * @param key key whose mapping is to be removed from the map.
  253.      * @return previous value associated with specified key, or <tt>null</tt>
  254.      *           if there was no entry for key.  (A <tt>null</tt> return can
  255.      *           also indicate that the map previously associated <tt>null</tt>
  256.      *           with the specified key, if the implementation supports
  257.      *           <tt>null</tt> values.)
  258.      * @throws UnsupportedOperationException if the <tt>remove</tt> operation
  259.      *           is not supported by this map.
  260.      */
  261.     public Object remove(Object key) {
  262.     Iterator i = entrySet().iterator();
  263.     Entry correctEntry = null;
  264.     if (key==null) {
  265.         while (correctEntry==null && i.hasNext()) {
  266.         Entry e = (Entry) i.next();
  267.         if (e.getKey()==null)
  268.             correctEntry = e;
  269.         }
  270.     } else {
  271.         while (correctEntry==null && i.hasNext()) {
  272.         Entry e = (Entry) i.next();
  273.         if (key.equals(e.getKey()))
  274.             correctEntry = e;
  275.         }
  276.     }
  277.  
  278.     Object oldValue = null;
  279.     if (correctEntry !=null) {
  280.         oldValue = correctEntry.getValue();
  281.         i.remove();
  282.     }
  283.     return oldValue;
  284.     }
  285.  
  286.  
  287.     // Bulk Operations
  288.  
  289.     /**
  290.      * Copies all of the mappings from the specified map to this map
  291.      * (optional operation).  These mappings will replace any mappings that
  292.      * this map had for any of the keys currently in the specified map.<p>
  293.      *
  294.      * This implementation iterates over the specified map's
  295.      * <tt>entrySet()</tt> collection, and calls this map's <tt>put</tt>
  296.      * operation once for each entry returned by the iteration.
  297.      *
  298.      * @param t mappings to be stored in this map.
  299.      * 
  300.      * @throws UnsupportedOperationException if the <tt>putAll</tt> operation
  301.      *           is not supported by this map.
  302.      * 
  303.      * @throws ClassCastException if the class of a key or value in the
  304.      *               specified map prevents it from being stored in this map.
  305.      * 
  306.      * @throws IllegalArgumentException if some aspect of a key or value in
  307.      *              the specified map prevents it from being stored in this map.
  308.      * 
  309.      * @throws NullPointerException this map does not permit <tt>null</tt>
  310.      *            keys or values, and the specified key or value is
  311.      *            <tt>null</tt>.
  312.      */
  313.     public void putAll(Map t) {
  314.     Iterator i = t.entrySet().iterator();
  315.     while (i.hasNext()) {
  316.         Entry e = (Entry) i.next();
  317.         put(e.getKey(), e.getValue());
  318.     }
  319.     }
  320.  
  321.     /**
  322.      * Removes all mappings from this map (optional operation). <p>
  323.      *
  324.      * This implementation calls <tt>entrySet().clear()</tt>.
  325.      *
  326.      * @throws    UnsupportedOperationException clear is not supported
  327.      *           by this map.
  328.      */
  329.     public void clear() {
  330.     entrySet().clear();
  331.     }
  332.  
  333.  
  334.     // Views
  335.  
  336.     private transient Set keySet = null;
  337.     /**
  338.      * Returns a Set view of the keys contained in this map.  The Set is
  339.      * backed by the map, so changes to the map are reflected in the Set,
  340.      * and vice-versa.  (If the map is modified while an iteration over
  341.      * the Set is in progress, the results of the iteration are undefined.)
  342.      * The Set supports element removal, which removes the corresponding entry
  343.      * from the map, via the Iterator.remove, Set.remove,  removeAll
  344.      * retainAll, and clear operations.  It does not support the add or
  345.      * addAll operations.<p>
  346.      *
  347.      * This implementation returns a Set that subclasses
  348.      * AbstractSet.  The subclass's iterator method returns a "wrapper
  349.      * object" over this map's entrySet() iterator.  The size method delegates
  350.      * to this map's size method and the contains method delegates to this
  351.      * map's containsKey method.<p>
  352.      *
  353.      * The Set is created the first time this method is called,
  354.      * and returned in response to all subsequent calls.  No synchronization
  355.      * is performed, so there is a slight chance that multiple calls to this
  356.      * method will not all return the same Set.
  357.      *
  358.      * @return a Set view of the keys contained in this map.
  359.      */
  360.     public Set keySet() {
  361.     if (keySet == null) {
  362.         keySet = new AbstractSet() {
  363.         public Iterator iterator() {
  364.             return new Iterator() {
  365.             private Iterator i = entrySet().iterator();
  366.  
  367.             public boolean hasNext() {
  368.                 return i.hasNext();
  369.             }
  370.  
  371.             public Object next() {
  372.                 return ((Entry)i.next()).getKey();
  373.             }
  374.  
  375.             public void remove() {
  376.                 i.remove();
  377.             }
  378.                     };
  379.         }
  380.  
  381.         public int size() {
  382.             return AbstractMap.this.size();
  383.         }
  384.  
  385.         public boolean contains(Object k) {
  386.             return AbstractMap.this.containsKey(k);
  387.         }
  388.         };
  389.     }
  390.     return keySet;
  391.     }
  392.  
  393.     private transient Collection values = null;
  394.     /**
  395.      * Returns a collection view of the values contained in this map.  The
  396.      * collection is backed by the map, so changes to the map are reflected in
  397.      * the collection, and vice-versa.  (If the map is modified while an
  398.      * iteration over the collection is in progress, the results of the
  399.      * iteration are undefined.)  The collection supports element removal,
  400.      * which removes the corresponding entry from the map, via the
  401.      * <tt>Iterator.remove</tt>, <tt>Collection.remove</tt>,
  402.      * <tt>removeAll</tt>, <tt>retainAll</tt> and <tt>clear</tt> operations.
  403.      * It does not support the <tt>add</tt> or <tt>addAll</tt> operations.<p>
  404.      *
  405.      * This implementation returns a collection that subclasses abstract
  406.      * collection.  The subclass's iterator method returns a "wrapper object"
  407.      * over this map's <tt>entrySet()</tt> iterator.  The size method
  408.      * delegates to this map's size method and the contains method delegates
  409.      * to this map's containsValue method.<p>
  410.      *
  411.      * The collection is created the first time this method is called, and
  412.      * returned in response to all subsequent calls.  No synchronization is
  413.      * performed, so there is a slight chance that multiple calls to this
  414.      * method will not all return the same Collection.
  415.      *
  416.      * @return a collection view of the values contained in this map.
  417.      */
  418.     public Collection values() {
  419.     if (values == null) {
  420.         values = new AbstractCollection() {
  421.         public Iterator iterator() {
  422.             return new Iterator() {
  423.             private Iterator i = entrySet().iterator();
  424.  
  425.             public boolean hasNext() {
  426.                 return i.hasNext();
  427.             }
  428.  
  429.             public Object next() {
  430.                 return ((Entry)i.next()).getValue();
  431.             }
  432.  
  433.             public void remove() {
  434.                 i.remove();
  435.             }
  436.                     };
  437.                 }
  438.  
  439.         public int size() {
  440.             return AbstractMap.this.size();
  441.         }
  442.  
  443.         public boolean contains(Object v) {
  444.             return AbstractMap.this.containsValue(v);
  445.         }
  446.         };
  447.     }
  448.     return values;
  449.     }
  450.  
  451.     /**
  452.      * Returns a set view of the mappings contained in this map.  Each element
  453.      * in this set is a Map.Entry.  The set is backed by the map, so changes
  454.      * to the map are reflected in the set, and vice-versa.  (If the map is
  455.      * modified while an iteration over the set is in progress, the results of
  456.      * the iteration are undefined.)  The set supports element removal, which
  457.      * removes the corresponding entry from the map, via the
  458.      * <tt>Iterator.remove</tt>, <tt>Set.remove</tt>, <tt>removeAll</tt>,
  459.      * <tt>retainAll</tt> and <tt>clear</tt> operations.  It does not support
  460.      * the <tt>add</tt> or <tt>addAll</tt> operations.
  461.      *
  462.      * @return a set view of the mappings contained in this map.
  463.      */
  464.     public abstract Set entrySet();
  465.  
  466.  
  467.     // Comparison and hashing
  468.  
  469.     /**
  470.      * Compares the specified object with this map for equality.  Returns
  471.      * <tt>true</tt> if the given object is also a map and the two maps
  472.      * represent the same mappings.  More formally, two maps <tt>t1</tt> and
  473.      * <tt>t2</tt> represent the same mappings if
  474.      * <tt>t1.keySet().equals(t2.keySet())</tt> and for every key <tt>k</tt>
  475.      * in <tt>t1.keySet()</tt>, <tt> (t1.get(k)==null ? t2.get(k)==null :
  476.      * t1.get(k).equals(t2.get(k))) </tt>.  This ensures that the
  477.      * <tt>equals</tt> method works properly across different implementations
  478.      * of the map interface.<p>
  479.      *
  480.      * This implementation first checks if the specified object is this map;
  481.      * if so it returns <tt>true</tt>.  Then, it checks if the specified
  482.      * object is a map whose size is identical to the size of this set; if
  483.      * not, it it returns <tt>false</tt>.  If so, it iterates over this map's
  484.      * <tt>entrySet</tt> collection, and checks that the specified map
  485.      * contains each mapping that this map contains.  If the specified map
  486.      * fails to contain such a mapping, <tt>false</tt> is returned.  If the
  487.      * iteration completes, <tt>true</tt> is returned.
  488.      *
  489.      * @param o object to be compared for equality with this map.
  490.      * @return <tt>true</tt> if the specified object is equal to this map.
  491.      */
  492.     public boolean equals(Object o) {
  493.     if (o == this)
  494.         return true;
  495.  
  496.     if (!(o instanceof Map))
  497.         return false;
  498.     Map t = (Map) o;
  499.     if (t.size() != size())
  500.         return false;
  501.  
  502.     Iterator i = entrySet().iterator();
  503.     while (i.hasNext()) {
  504.         Entry e = (Entry) i.next();
  505.         Object key = e.getKey();
  506.         Object value = e.getValue();
  507.         if (value == null) {
  508.         if (!(t.get(key)==null && t.containsKey(key)))
  509.             return false;
  510.         } else {
  511.         if (!value.equals(t.get(key)))
  512.             return false;
  513.         }
  514.     }
  515.     return true;
  516.     }
  517.  
  518.     /**
  519.      * Returns the hash code value for this map.  The hash code of a map is
  520.      * defined to be the sum of the hash codes of each entry in the map's
  521.      * <tt>entrySet()</tt> view.  This ensures that <tt>t1.equals(t2)</tt>
  522.      * implies that <tt>t1.hashCode()==t2.hashCode()</tt> for any two maps
  523.      * <tt>t1</tt> and <tt>t2</tt>, as required by the general contract of
  524.      * Object.hashCode.<p>
  525.      *
  526.      * This implementation iterates over <tt>entrySet()</tt>, calling
  527.      * <tt>hashCode</tt> on each element (entry) in the Collection, and adding
  528.      * up the results.
  529.      *
  530.      * @return the hash code value for this map.
  531.      * @see Map.Entry#hashCode()
  532.      * @see Object#hashCode()
  533.      * @see Object#equals(Object)
  534.      * @see Set#equals(Object)
  535.      */
  536.     public int hashCode() {
  537.     int h = 0;
  538.     Iterator i = entrySet().iterator();
  539.     while (i.hasNext())
  540.         h += i.next().hashCode();
  541.     return h;
  542.     }
  543.  
  544.     /**
  545.      * Returns a string representation of this map.  The string representation
  546.      * consists of a list of key-value mappings in the order returned by the
  547.      * map's <tt>entrySet</tt> view's iterator, enclosed in braces
  548.      * (<tt>"{}"</tt>).  Adjacent mappings are separated by the characters
  549.      * <tt>", "</tt> (comma and space).  Each key-value mapping is rendered as
  550.      * the key followed by an equals sign (<tt>"="</tt>) followed by the
  551.      * associated value.  Keys and values are converted to strings as by
  552.      * <tt>String.valueOf(Object)</tt>.<p>
  553.      *
  554.      * This implementation creates an empty string buffer, appends a left
  555.      * brace, and iterates over the map's <tt>entrySet</tt> view, appending
  556.      * the string representation of each <tt>map.entry</tt> in turn.  After
  557.      * appending each entry except the last, the string <tt>", "</tt> is
  558.      * appended.  Finally a right brace is appended.  A string is obtained
  559.      * from the stringbuffer, and returned.
  560.      *
  561.      * @return a String representation of this map.
  562.      */
  563.     public String toString() {
  564.     int max = size() - 1;
  565.     StringBuffer buf = new StringBuffer();
  566.     Iterator i = entrySet().iterator();
  567.  
  568.     buf.append("{");
  569.     for (int j = 0; j <= max; j++) {
  570.         Entry e = (Entry) (i.next());
  571.         buf.append(e.getKey() + "=" + e.getValue());
  572.         if (j < max)
  573.         buf.append(", ");
  574.     }
  575.     buf.append("}");
  576.     return buf.toString();
  577.     }
  578. }
  579.